/**
 * Licensed to Open-Ones Group under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Open-Ones Group licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a
 * copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package openones.ipresenter.android.view;

import java.io.IOException;
import java.util.Set;
import java.util.UUID;

import openones.ipresenter.android.R;
import openones.ipresenter.android.dao.ConnectionData;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * This activity is used for manipulating with Bluetooth. It includes Get Paired Devices, Scan For New Devices.
 * Start Presentation when a connection is made.
 * @author OOG Member
 */
public class ScanBluetoothActivity extends Activity {
    /**
     * The UUID is the same on the server.
     */
    private static final UUID MY_UUID = UUID.fromString("7A51FDC2-FDDF-4c9b-AFFC-98BCD91BF93B");

    /**
     * The length of Bluetooth MAC address, used to get the BluetoothDevice.
     */
    private static final int MAC_ADDRESS_LENGTH = 17;
    
    
    /**
     * Constant tells Bluetooth is not supported by the device.
     */
    private static final int BLUETOOTH_UNAVAILABLE = 0;
    
    /**
     * Constant tells Bluetooth is disabled.
     */
    private static final int BLUETOOTH_DISABLED = 1;
    
    /**
    * Constant tells Bluetooth is enabled.
    */
   private static final int BLUETOOTH_ENABLED = 2;
   
   
   /**
    * Constant tells there's no connection.
    */
   private static final int NOT_CONNECTED = 0;
   
   /**
    * Constant tells connection is established successfully.
    */
   private static final int CONNECTED = 1;
   
   
   /**
    * Intent requests to enable bluetooth.
    */
   private static final int REQUEST_ENABLE_BT = 1;
   
   private static final int BRIGHTNESS_DIALOG = 1;
   
   
   /**
    * The current bluetooth state (unavailable, disabled, enabled).
    */
   private int mBluetoothState;
   
   /**
    * The current connection state (not connected, connecting, connected).
    */
   private int mConnectionState;
   
   
   /**
    * Used to update lvDevices with data fetched from the bluetooth devices discovered.
    */
   private ArrayAdapter<String> mDevicesArrayAdapter;
   
    /**
     * The TextView shows the remote bluetooth device.
     */
    private TextView tvRemoteDevice;

    
    /**
     * The local bluetooth adapter.
     */
    private BluetoothAdapter mBtAdapter;
    
    /**
     * The remote bluetooth device.
     */
    private BluetoothDevice mDevice;
    
    /**
     * The bluetooth socket.
     */
    private BluetoothSocket mSocket;
    
    /**
     * Used to received broadcast intent.
     */
    private BroadcastReceiver mReceiver;
    
    /**
     * The progress dialog displayed when connecting task is in progress.
     */
    private ProgressDialog mProgressDialog;

    /**
     * Called when the activity is first created.
     * @param savedInstanceState Information about the last state.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.scanbluetooth);
        
        setupUI();
        setupDevicesList();
        setupBluetooth();
        
    }
    
    /**
     * setup the views on the layout.
     * Set the OnClickLisnter for the buttons.
     * Set the OnItemClickListener for the items on the lvDevices.
     */
    void setupUI() {
        Button btGetPairedDevices = (Button) findViewById(R.id.scanbt_btGetPairedDevices);
        if (btGetPairedDevices != null) {
            btGetPairedDevices.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    getPairedDevices();
                }
            });
        }

        Button btScanNewDevices = (Button) findViewById(R.id.scanbt_btScanNewDevices);
        if (btScanNewDevices != null) {
            btScanNewDevices.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    scanNewDevices();
                }
            });
        }
        
        Button btStartPresentation = (Button) findViewById(R.id.scanbt_btStartPresentation);
        if (btStartPresentation != null) {
            btStartPresentation.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    showDialog(BRIGHTNESS_DIALOG);
                }
            });
        }
    
        tvRemoteDevice = (TextView) findViewById(R.id.scanbt_tvRemoteDevice);
    }
    
    /**
     * Setup lvDevices.
     * Handle its OnItemClick event.
     */
    void setupDevicesList() {
        ListView lvDevices = (ListView) findViewById(R.id.scanbt_lvDevices);
        
        mDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
        if (lvDevices != null) {
            lvDevices.setAdapter(mDevicesArrayAdapter);
            lvDevices.setOnItemClickListener(new OnItemClickListener() {
                public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
                    //if click on No Devices Found, do nothing
                    if (((TextView) v).getText().toString() == "No device found") {
                        //toast
                        Toast.makeText(ScanBluetoothActivity.this, "No device found", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    
                    // Get the device MAC address, which is the last 17 chars in the View
                    String info = ((TextView) v).getText().toString();
                    final String address = info.substring(info.length() - MAC_ADDRESS_LENGTH);
                    final String name = info.substring(0, info.length() - MAC_ADDRESS_LENGTH - 2);
                    
                    AlertDialog.Builder builder = new AlertDialog.Builder(ScanBluetoothActivity.this);
                    builder.setMessage("Are you sure you want to connect to " + name + " ?")
                           .setCancelable(false)
                           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                               public void onClick(DialogInterface dialog, int id) {
                                   if (mBluetoothState == BLUETOOTH_ENABLED) {
                                       // Cancel discovery because it's costly and we're about to connect
                                       mBtAdapter.cancelDiscovery();

                                       mDevice = mBtAdapter.getRemoteDevice(address);
                                       if (mDevice != null) {
                                           mProgressDialog = ProgressDialog.show(ScanBluetoothActivity.this, 
                                                   "Connecting", "Please wait ...");
                                           mProgressDialog.setCancelable(true);
                                           
                                           // If user wants to cancel current connection and start another one.
                                           // Cancel current connection if it exists.
                                           if (mConnectionState == CONNECTED) {
                                               mConnectionState = NOT_CONNECTED;
                                               closeSocket();
                                           }
                                           
                                           doConnectToBluetoothDeviceTask();
                                       } else {
                                           Toast.makeText(ScanBluetoothActivity.this, 
                                                   "Get device fails", Toast.LENGTH_SHORT).show();
                                       }
                                   }
                               }
                           })
                           .setNegativeButton("No", new DialogInterface.OnClickListener() {
                               public void onClick(DialogInterface dialog, int id) {
                                    dialog.cancel();
                               }
                           });
                    AlertDialog alert = builder.create();
                    alert.show();  
                }
            });
        }
        
        mProgressDialog = null;
    }
    
    /**
     * Connect to Bluetooth Device (mBluetoothDevice).
     */
    void doConnectToBluetoothDeviceTask() {
        AsyncTask<Void, Void, Void> connectTask = new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    mSocket = mDevice.createRfcommSocketToServiceRecord(MY_UUID);
                } catch (IOException e) {
                    mConnectionState = NOT_CONNECTED;
                    ScanBluetoothActivity.this.runOnUiThread(new Runnable() {
                        public void run() {
                            Toast.makeText(ScanBluetoothActivity.this, 
                                    "Create connection fails", Toast.LENGTH_SHORT).show();
                        }
                    });
                } 
                
                if (mSocket != null) {
                    try {
                        mConnectionState = CONNECTED;
                        mSocket.connect();
                    } catch (final IOException e) {
                        mConnectionState = NOT_CONNECTED;
                        
                        ScanBluetoothActivity.this.runOnUiThread(new Runnable() {
                            public void run() {
                                Toast.makeText(ScanBluetoothActivity.this, 
                                        "Connect to device fails", Toast.LENGTH_SHORT).show();
                            }
                        });

                        closeSocket();
                    }
                }
                return null;
            }
        
            @Override
            protected void onPostExecute(Void result) {
                if (mProgressDialog != null) {
                    mProgressDialog.dismiss();
                }
                
                if (mConnectionState == CONNECTED && mSocket != null) {
                    //update tvRemoteDevice
                    String name = mSocket.getRemoteDevice().getName().toString();
                    tvRemoteDevice.setText(name);
                    
                    AlertDialog.Builder builder = new AlertDialog.Builder(ScanBluetoothActivity.this);
                    builder.setMessage("Successfully connected to " + name + ".\n" 
                                + "You may start presentation now.")
                           .setCancelable(false)
                           .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                               public void onClick(DialogInterface dialog, int id) {
                                   dialog.cancel();
                               }
                           });
                    AlertDialog alert = builder.create();
                    alert.show();
                } else {
                    // Set the tvRemoteDevice to indicate that no connection with remote device is made.
                    tvRemoteDevice.setText("NOT CONNECTED");
                }
            }
                
        };
        connectTask.execute();
    }
    
    /**
     * Setup Bluetooth state (unavailable, unabled, disabled).
     * Register Broadcast Receiver to receive information about a device when it is found.
     */
    void setupBluetooth() {
        mConnectionState = NOT_CONNECTED;
        
        if (mDevice != null) {
            mDevice = null; 
        }
        if (mSocket != null) {
            mSocket = null;
        }
        
        mBtAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBtAdapter == null) {
            Toast.makeText(this, "Bluetooth is unavailable", Toast.LENGTH_SHORT).show();
            mBluetoothState = BLUETOOTH_UNAVAILABLE;

        } else {
            if (mBtAdapter.isEnabled()) {
                mBluetoothState = BLUETOOTH_ENABLED;
            } else {
                mBluetoothState = BLUETOOTH_DISABLED;
                Toast.makeText(this, "Bluetooth is disabled.\n"
                        + "You must enable Bluetooth to work.", Toast.LENGTH_SHORT).show();
            }
        }
    
        mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();

                // When discovery finds a device
                if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                    // Get the BluetoothDevice object from the Intent
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    mDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                    mDevicesArrayAdapter.notifyDataSetChanged();
                } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                    if (mDevicesArrayAdapter.getCount() == 0) {
                        String noDevices = "No device found";
                        mDevicesArrayAdapter.add(noDevices);
                        mDevicesArrayAdapter.notifyDataSetChanged();
                    }
                }
            }
        };
        
        // Register for broadcasts when a device is discovered
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        this.registerReceiver(mReceiver, filter);

        // Register for broadcasts when discovery has finished
        filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        this.registerReceiver(mReceiver, filter);
    
    }
    
    
    
    /**
     * Get devices that has been paired with this device before.
     */
    void getPairedDevices() {
        if (mBluetoothState == BLUETOOTH_UNAVAILABLE) {
            //Toast not support bluetooth
            Toast.makeText(this, "Bluetooth is unavailable", Toast.LENGTH_SHORT).show();
            return;
            
        }
    
        if (mBluetoothState == BLUETOOTH_DISABLED) { 
            //Toast bluetooth disable, enable ?
            enableBluetooth();
            return;
        }
        
        //clear current devices list
        mDevicesArrayAdapter.clear();
        mDevicesArrayAdapter.notifyDataSetChanged();
        
        // Get a set of currently paired devices
        Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();

        // If there are paired devices, add each one to the ArrayAdapter
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                mDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                mDevicesArrayAdapter.notifyDataSetChanged();
            }
        } else {
            String noDevices = "No device found";
            mDevicesArrayAdapter.add(noDevices);
            mDevicesArrayAdapter.notifyDataSetChanged();
        }
        
    }
    
    /**
     * Scan available device in the local area.
     */
    void scanNewDevices() {
        if (mBluetoothState == BLUETOOTH_UNAVAILABLE) {
            //Toast not support bluetooth
            Toast.makeText(this, "Bluetooth is unavailable", Toast.LENGTH_SHORT).show();
            return;
            
        }
        
        if (mBluetoothState == ScanBluetoothActivity.BLUETOOTH_DISABLED) {
            enableBluetooth();
            return;
        }
        
        //clear current device list
        mDevicesArrayAdapter.clear();
        mDevicesArrayAdapter.notifyDataSetChanged();
        
        // If we're already discovering, stop it
        if (mBtAdapter.isDiscovering()) {
            mBtAdapter.cancelDiscovery();
        }
        
        // Request discover from BluetoothAdapter
        mBtAdapter.startDiscovery();
    }
    
    /**
     * If a connection is made successfully, allow user to start presentation.
     * Go to PresentActivity. 
     */
    void startPresentation() {
        // Need to check connection state first.
        if (mConnectionState == NOT_CONNECTED || mSocket == null) {
            //toast
            Toast.makeText(this, "Not connected.\n"
                    + "You must connect to a device first", Toast.LENGTH_SHORT).show();
            return;
        }
        
        
        Intent intent = new Intent(ScanBluetoothActivity.this, PresentActivity.class);
        intent.putExtra("connectionType", PresentActivity.CONNECTION_TYPE_BLUETOOTH);
        ConnectionData connectionData = ((ConnectionData) getApplicationContext());
        connectionData.setBluetoothSocket(mSocket);
        startActivity(intent);
        finish();
    }
    
    /**
     * Request enable Bluetooth.
     * The result of this request is received in onActivityResult.
     */
    void enableBluetooth() {
        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    }
    

    /**
    * Create Option menu.
    * @param menu The menu to create.
    * @return boolean Indicate if the menu is created successfully.
    */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.scanbluetooth, menu);
        return true;
    }
    
    /**
     * Called when an item on the menu is selected.
     * @param item The menu item being selected.
     * @return boolean Indicate whether the OnItemClick is handled.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_mainmenu:
            // Free socket and stop discovering.
            freeResource();
            
            // Return to MainMenuActivity
            Intent intentMainMenu = new Intent(ScanBluetoothActivity.this, MainMenuActivity.class);
            startActivity(intentMainMenu);
            finish();
            return true;
        case R.id.menu_wifi:
            // Free socket and stop discovering.
            freeResource();
            
            // Switch to ScanWifi mode  
            Intent intentScanWifi = new Intent(ScanBluetoothActivity.this, ScanWifiActivity.class);
            startActivity(intentScanWifi);
            finish();
            return true;
        default:
            break;
        }
        return false;
    }
    
    /**
     * Called when the activity started by this activity returns result.
     * @param requestCode request
     * @param resultCode Result code
     * @param data Data associated with Intent.
     * 
     */
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // Bluetooth is now enabled
                mBluetoothState = BLUETOOTH_ENABLED;
                Toast.makeText(this, "Enable Bluetooth succeeds", Toast.LENGTH_SHORT).show();
            } else {
                // User did not enable Bluetooth or an error occured
                Toast.makeText(this, "Enable Bluetooth fails", Toast.LENGTH_SHORT).show();
            }
        default:
            break;
        }
    }
    
    /**
     * Free any resource this Activity is using.
     */
    public void freeResource() {
        // If we're discovering, stop it
        if (mBtAdapter != null  && mBtAdapter.isDiscovering()) {
            mBtAdapter.cancelDiscovery();
        }
        
        // Close the current socket.
        closeSocket();
    }
    
    /**
     * Close the socket if it exists.
     */
    public void closeSocket() {
        // Close socket
        if (mSocket != null) {
            try {
                mSocket.close();
            } catch (IOException e) {
                ScanBluetoothActivity.this.runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(ScanBluetoothActivity.this, 
                                "Close connection fails", Toast.LENGTH_SHORT).show();
                    }
                });
            }
            mSocket = null;
        }
    }
    
 // a dialog appears after user tap Brightness item 
    @Override
    protected Dialog onCreateDialog(int id) {
        // TODO Auto-generated method stub
        switch (id) {
        case BRIGHTNESS_DIALOG:
            return new AlertDialog.Builder(ScanBluetoothActivity.this).setTitle(
                    "Turn on battery saver mode?").setPositiveButton("OK",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            /* User clicked OK so do some stuff */
                            setBrightness();
                            startPresentation();
                        }
                    }).setNegativeButton("Cancel",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog,
                                int whichButton) {
                            /* User clicked Cancel so do some stuff */
                            startPresentation();
                        }
                    }).create();
        }
        return null;
    }

    // set the brightness of application is 1
    public void setBrightness() {
        android.provider.Settings.System.putInt(getContentResolver(), android.provider.Settings.System.SCREEN_BRIGHTNESS, 1);
        float backLightValue = (float) 1 / 100;
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.screenBrightness = backLightValue;
        getWindow().setAttributes(layoutParams);
    }
}